package models

import (
	db "hulk-api/databases"
	"fmt"
	"hulk-api/util"
	"os"
	"strings"
	"os/exec"
	"encoding/json"
	"time"
	"math"
	"github.com/garyburd/redigo/redis"
	"hulk-api/util/common"
)

type DeplaymentItem struct {
	Name string 		`json:"name"`
	DeployMode string 	`json:"deploymode"`
	Describe string 	`json:"describe"`
	GitUrl string		`json:"giturl"`
	Branch string		`json:"branch"`
	CodeLang string		`json:"codelang"`
	Compile string		`json:"compile"`
	Hosts string		`json:"hosts"`
	YamlContent string 	`json:"yamlcontent"`
	YamlPath string		`json:"yamlpath"`
	LastStatus string	`json:"laststatus"`
	LastSuccess string	`json:"lastsuccess"`
	LastFail string		`json:"lastfail"`
	LastDurtime int64	`json:"lastdurtime"`
	StartTime string 	`json:"starttime"`

}









func (item *DeplaymentItem) loadDeployItemByname() (  err error) {

	row := db.SqlDB.QueryRow("select deploy_mode,note,git_url,git_branch,code_lang,compile,hosts,yaml_path,last_status,last_success,last_fail,last_exec_time from ansibleDeploymentItem where name=?" , item.Name)
	if err =row.Scan(&item.DeployMode,&item.Describe,&item.GitUrl,&item.Branch,&item.CodeLang,&item.Compile,&item.Hosts,&item.YamlPath,&item.LastStatus,&item.LastSuccess,&item.LastFail,&item.LastDurtime); err != nil{
		fmt.Printf("scan failed, err:%v",err)
		return
	}
	return


}

func(item DeplaymentItem) IsOngoing() ( ongoing bool ,err error)  {
	r_key := "deploying-" + item.Name
	exist, err := redis.Int(db.RedisPool.Get().Do("EXISTS",r_key))
	if exist==1 {
		fmt.Println("ongoning is true")
		ongoing = true
	} else {
		fmt.Println("ongoning is false")
		ongoing = false
	}
	return
}


func (item DeplaymentItem) Start() (thisBuildJobDir string ,  err error) {
	//redis写入key,实现锁机制
	item.setRedisKey()
	defer item.deleteRedisKey()

	starTime := time.Now()
	item.beginDeplay()
	defer item.closeDeplay()  //让前端轮训此次构建的完成

	//STEP 1 : git pull
	fmt.Println("debug start deploy:" , item.Name)
	workPath := strings.TrimSpace(item.YamlPath)[1:]
	fmt.Println("工作空间 :" , workPath)
	//exec.Command("git" ,"init" , )
	//入库统计 go item.insert()
	bdir := workPath + "/builds"
	//var thisBuildJobDir string
	err = common.CreateIfNotExist(bdir)
	if err != nil {
		fmt.Println("create build dir error is :" , err.Error())
		return
	} else {
		thisBuildJobDir,_ = common.BuildPath(bdir).CreateNextBuildDir()
	}
	//thisBuildJobDir = "1"  //windows 测试行代码
	outPutFilePath := bdir + "/" + thisBuildJobDir + "/" + "output.txt"
	f,err := os.OpenFile(outPutFilePath,os.O_RDWR | os.O_CREATE|os.O_APPEND,0666)
	defer f.Close()
	//defer f.Sync()
	if err != nil {
		fmt.Println("create file is error")
	} else {
		f.WriteString("Building in workspace " + workPath + "\n")
	}
	//判断.git本地仓库是否存在
	//如果第一次需要git init
	codeDir := workPath + "/code"    //创建工作目录
	common.CreateIfNotExist(codeDir)

	f.WriteString(" > git rev-parse --is-inside-work-tree \n")
	cmd := exec.Command("git" ,"rev-parse" , "--is-inside-work-tree")
	//cmd := exec.Command("git" ,"init" )
	cmd.Dir = codeDir
	out, err := cmd.CombinedOutput()
	fmt.Println("has git dir ? " , string(out))
	if err != nil {
		fmt.Println("error  is " , err.Error())
	}

	if strings.TrimSpace(string(out)) != "true" {
		//git init
		f.WriteString(" > git init \n")
		cmd := exec.Command("git" ,"init" )
		//cmd := exec.Command("git" ,"init" )
		cmd.Dir = codeDir
		out, err := cmd.CombinedOutput()
		f.WriteString(string(out) + "\n")
		if err != nil {
			fmt.Println("error  is " , err.Error())
		}
	}
	//git config remote url
	f.WriteString(fmt.Sprintf(" > git config remote.origin.url %s \n " , item.GitUrl))
	cmd = exec.Command("git" ,"config" , "remote.origin.url",item.GitUrl)  //arg不能包含空格
	//cmd := exec.Command("git" ,"init" )
	cmd.Dir = codeDir
	out, err = cmd.CombinedOutput()
	fmt.Println("git config setting output is " , string(out))
	f.WriteString(string(out) + "\n")
	if err != nil {
		fmt.Println("git config setting error  is " , err.Error())
	}
	//获取分支

	//git fetch --tags --progress http://gitlab.jtljia.com/devops/db-sql.git +refs/heads/*:refs/remotes/origin/*   没有输出
	f.WriteString(fmt.Sprintf(" > git fetch --tags --progress %s +refs/heads/*:refs/remotes/origin/* \n" , item.GitUrl))
	cmd = exec.Command("git" ,"fetch" , "--tags","--progress",item.GitUrl,"+refs/heads/*:refs/remotes/origin/*")
	//cmd := exec.Command("git" ,"init" )
	cmd.Dir = codeDir
	out, err = cmd.CombinedOutput()
	fmt.Println("获取分支内容 " , string(out))
	f.WriteString(string(out) + "\n")
	if err != nil {
		fmt.Println("error  is " , err.Error())
	}
	//获取指定分支最后一次commit id
	f.WriteString(fmt.Sprintf(" > git rev-parse refs/remotes/origin/%s^{commit} \n" , item.Branch))
	cmd = exec.Command("git" ,"rev-parse" , "refs/remotes/origin/" + strings.TrimSpace(item.Branch) +  "^{commit}")
	//cmd := exec.Command("git" ,"init" )
	cmd.Dir = codeDir
	out, err = cmd.CombinedOutput()
	fmt.Println("master commit id is " , string(out))
	//f.WriteString(string(out) + "\n")
	if err != nil {
		fmt.Println("error  is " , err.Error())
	}
	//拉取代码
	f.WriteString(fmt.Sprintf(" > git checkout -f %s \n" , strings.TrimSpace(string(out))))
	cmd = exec.Command("git" ,"checkout" ,"-f", strings.TrimSpace(string(out)))
	//cmd := exec.Command("git" ,"init" )
	cmd.Dir = codeDir
	out, err = cmd.CombinedOutput()
	fmt.Println("拉取结果是 " , string(out))
	f.WriteString(string(out) + "\n")
	if err != nil {
		fmt.Println("error  is " , err.Error())
	}

	//编译应该不能异步的
	f.WriteString(fmt.Sprintf(" > 编译  sleep 5 \n" ))
	fmt.Println("编译  sleep 5 ",item.Compile)  //golang 先省略
	time.Sleep(20 * time.Second)
	//开始执行脚本
	f.WriteString(fmt.Sprintf(" > 开始执行任务 \n" ))

	f.WriteString(fmt.Sprintf(" + ansible-playbook -i hosts %s.yaml \n" , item.Name ))
	//cmd = exec.Command("ansible-playbook" ,"-i hosts" , strings.TrimSpace(item.Name) +  ".yaml")
	cmd = exec.Command("/bin/bash" ,"-c" , "ansible-playbook -i hosts " + strings.TrimSpace(item.Name) +  ".yaml")
	//cmd := exec.Command("git" ,"init" )
	cmd.Dir = workPath
	out, err = cmd.CombinedOutput()
	fmt.Println("the result is ")
	fmt.Println(string(out))
	fmt.Println("the result is down ")
	if err != nil {
		f.WriteString(fmt.Sprintf(" > 任务失败：\n %s \n" , err.Error() ))
		fmt.Println(" exec error  is " , err.Error())
		item.deployFail(starTime)
		return
	} else {
		f.WriteString(string(out))
		item.deploySuccess(starTime)
	}


	return

}

func (item DeplaymentItem) LoadWorkspace() ( data string , err error) {
	//入库
	workspace := "workspace/" + item.Name + "/code"
	fmt.Println(workspace)
	dirStruct := util.ParseDir(workspace)
	dataByte,err := json.Marshal(dirStruct)
	if err != nil {
		fmt.Println("err is " , err.Error())
	}
	//fmt.Println(string(dataByte))
	data = string(dataByte)


	return

}


func (item DeplaymentItem) Delete() (  err error) {
	//入库
	fmt.Println("item name" , item.Name)
	fmt.Println("yamlpath is " , item.YamlPath)

	//stmt, err := db.SqlDB.Prepare("insert into ansibleDeploymentItem(name,deploy_mode,note,git_url,git_branch,code_lang,compile,hosts,yaml_path) values (?,?,?,?,?,?,?,?,?)")
	stmt, err := db.SqlDB.Prepare("delete from ansibleDeploymentItem where name=?")
	if err != nil {
		fmt.Println("here error is ..." , err.Error())
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec(item.Name)
	if err != nil {
		fmt.Println("delete error" , err.Error())
		return
	}
	// go delete workspace
	workPath := "." + strings.TrimSpace(item.YamlPath)
	errr := os.RemoveAll(workPath)
	if err != nil {
		fmt.Println("remove dir err: " , errr.Error())
	}
	return
}

func (item DeplaymentItem) Clean() (  err error) {
	//入库
	fmt.Println("yamlpath is " , item.YamlPath)
	workPath := "." + strings.TrimSpace(item.YamlPath) + "/code"
	err = util.CleanDir(workPath)
	return
}

func (item *DeplaymentItem) beginDeplay() (  ) {
	//入库,状态设为构建状态
	//并且将以此为持续构建时间推论
	stmt, err := db.SqlDB.Prepare("update ansibleDeploymentItem set status=1 , start_time=? where name=?")
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec(time.Now().Format("2006-01-02 15:04:05"),item.Name)
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}

}

func (item *DeplaymentItem) deleteRedisKey() (  ) {
	r_key := "deploying-" + item.Name
	fmt.Println("开始删除key")
	_, err := db.RedisPool.Get().Do("DEL", r_key)
	if err != nil {
		fmt.Println("redis del error:", err)   //暂时不做处理
	}

}
func (item *DeplaymentItem) setRedisKey() (  ) {
	r_key := "deploying-" + item.Name
	_, err := db.RedisPool.Get().Do("SET", r_key, "starting")
	if err != nil {
		fmt.Println("redis set error:", err)   //暂时不做处理
	}

}

func (item *DeplaymentItem) closeDeplay() (  ) {
	//入库,状态设为未构建
	stmt, err := db.SqlDB.Prepare("update ansibleDeploymentItem set status=0 where name=?")
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec(item.Name)
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}

}
func (item *DeplaymentItem) deployFail(starttime time.Time) (  ) {
	//入库,状态设为未构建
	//TODO: 更新持续构建时间
	exec := math.Floor(time.Now().Sub(starttime).Seconds())
	stmt, err := db.SqlDB.Prepare("update ansibleDeploymentItem set last_fail=? , last_status='失败' , last_exec_time=? where name=?")
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}
	defer stmt.Close()

	_, err = stmt.Exec(time.Now().Format("2006-01-02 15:04:05"),exec,item.Name)
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}

}
func (item *DeplaymentItem) deploySuccess(starttime time.Time) (  ) {
	//入库,状态设为未构建
	//TODO: 更新持续构建时间
	exec := math.Floor(time.Now().Sub(starttime).Seconds())
	stmt, err := db.SqlDB.Prepare("update ansibleDeploymentItem set last_success=? , last_status='成功', last_exec_time=? where name=?")
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec(time.Now().Format("2006-01-02 15:04:05"),exec,item.Name)
	if err != nil {
		fmt.Println("更新构建状态失败，错误：" , err.Error())
		//设置任务失败状态？
		return
	}

}



//构建进度条展示






type DeplaymentJob struct {
	Id int 					`json:"id"`
	Title string 			`json:"title"`
	Content string 			`json:"content"`
	OnlineTime string 		`json:"onlinetime"`
	ItemList string 		`json:"itemlist"`
	DbList string 			`json:"dblist"`
	SqlPathList string 		`json:"sqlpathlist"`
	CreateTime string 		`json:"createtime"`
	SqlList string 			`json:"sqllist"`
	Status string			`json:"status"`
}



//任务发布状态
type Status struct {
	Sqlloading bool						`json:"sqlloading"`
	Dbjob map[string]IconStyle			`json:"dbjob"`
	Itemloading bool					`json:"itemloading"`
	Itemjob map[string]IconStyle		`json:"itemjob"`
}

type IconStyle struct {
	Icon string							`json:"icon"`
	IconType string						`json:"type"`
	Result string						`json:"result"`   //执行结果，存放在redis中
	Button bool							`json:"button"`   //能否手动执行按钮，存放在redis中
}










//暂时模拟操作




func (job DeplaymentJob) Close()   {
	_, e := db.SqlDB.Exec("update deployment_job set status=2 where id=?"  , job.Id)
	if e != nil {
		fmt.Println("设置中止状态失败。。。 ")
	}
}

func (job SqlJob) Run()  {
	fmt.Println("this handel band job is " , job)
	//step 1 : 获取当前状态from redis
	s,err := redisGet(job.Id)
	if err != nil {
		fmt.Println("获取当前任务状态失败")
		//TODO：作何处理
	}
	var style Status
	json.Unmarshal([]byte(s),&style)
	fmt.Printf("当前任务流的状态是   %+v\n", style)
	//开始执行
	style.Dbjob[job.Dbname] = IconStyle{
		Icon: "el-icon-loading",
		IconType: "primary",
		Result: "执行中",
		Button: false,
	}
	//style.Dbjob[job.Dbname].Icon = "" map 无法直接获取列


	redisSet(job.Id,style) //异步
	//假设需要10秒
	//time.Sleep(10*time.Second)

	_,err = job.DoBySqlpath()  //暂时忽略成功的执行结果 _
	if err != nil {
		style.Dbjob[job.Dbname] = IconStyle{
			Icon: "el-icon-close",
			IconType: "danger",
			Result: err.Error(),
			Button: false,
		}
		redisSet(job.Id,style) //异步

		return //遇错，即终止任务的继续执行
	}

	style.Dbjob[job.Dbname] = IconStyle{
		Icon: "el-icon-check",
		IconType: "success",
		Result: "执行成功",
		Button: true,
	}
	redisSet(job.Id,style) //异步

}

func (item DeplaymentItem) Run(deploymentid int)  {

	//step 1 : 获取当前状态from redis
	s,err := redisGet(deploymentid)
	if err != nil {
		fmt.Println("获取当前任务状态失败")
		//TODO：作何处理
	}
	var style Status
	json.Unmarshal([]byte(s),&style)
	fmt.Printf("当前任务流的状态是   %+v\n", style)

	//加载任务
	errr := item.loadDeployItemByname()
	if errr != nil {
		fmt.Println("load item by name is fail " , item.Name)
	}
	fmt.Println("WWWWWWWWWWWWWWWWWill be start job workflow " , item)

	for {
		is,_ := item.IsOngoing() //忽略redis返回的错误，加锁失败
		if is {
			fmt.Println("该项目正在构建中，请稍后....")
			time.Sleep(1 * time.Second)
		}else {
			fmt.Println("即将构建项目 ：" , item.Name)
			break
		}
	}

	//开始执行
	style.Itemjob[item.Name] = IconStyle{
		Icon: "el-icon-loading",
		IconType: "primary",
		Result: "执行中",
		Button: false,
	}
	//style.Dbjob[job.Dbname].Icon = "" map 无法直接获取列
	redisSet(deploymentid,style) //异步

	var id string  //任务重item构建的ID; output id
	id,err = item.Start()

	if err != nil {
		fmt.Println("item job start statussssssssssssssss is " , err.Error())
		style.Itemjob[item.Name] = IconStyle{
			Icon: "el-icon-close",
			IconType: "danger",
			//Result: err.Error(), 不直接返回错误 直接返回工作空间的ID
			Result: "Output: #" + id,
			Button: false,
		}
		redisSet(deploymentid,style) //异步
		return //遇错，即终止任务的继续执行
	}
	//time.Sleep(20 * time.Second)  //模拟5秒完成,并成功
	style.Itemjob[item.Name] = IconStyle{
		Icon: "el-icon-check",
		IconType: "success",
		Result: "发布成功",
		Button: true,
	}
	redisSet(deploymentid,style)

}














